<template>
	<view ref="eTransition" :class="classes" :style="[mergeStyle]" @touchmove="noop">
		<slot />
	</view>
</template>

<script lang="ts">
	import { defineComponent } from 'vue'
	import mpMixin from '../../libs/mixin/mpMixin'
	export default defineComponent({
		name: 'transition',
		mixins: [mpMixin]
	})
</script>

<script lang="ts" setup>
	import {
		ref,
		watch,
		computed,
		nextTick,
	} from "vue";
	import {
		nextTick as _nextTick
	} from "../e-transition/transition";
	import {
		sleep
	} from "../../libs/utils";
	import {
		noop
	} from "../../libs/utils/_utils";
	import type {
		StyleProperties
	} from "../../libs/tokens";
	// #ifndef APP-NVUE
	import {
		getClassNames
	} from "../e-transition/transition";
	// #endif
	// #ifdef APP-NVUE
	import {
		animation,
		getStyle
	} from "../e-transition/transition";
	// #endif

	const props = defineProps({
		// 是否展示组件
		show: {
			type: Boolean,
			default: false
		},
		// 动画的执行时间，单位ms
		duration: {
			type: [String, Number],
			default: 300
		},
		// 使用的动画过渡函数
		timingFunction: {
			type: String,
			default: "ease-out",
		},
	})



	const eTransition = ref<any>(null)

	const classes = ref("");
	const viewStyle = ref<StyleProperties>({
		opacity: 0,
	});
	let transitionEnded = false;
	let display = false;

	const mergeStyle = computed(() => ({
		// #ifndef APP-NVUE
		transitionDuration: `${props.duration}ms`,
		// display: `${this.display ? '' : 'none'}`,
		transitionTimingFunction: props.timingFunction,
		// #endif
		...viewStyle.value,
	}));
	// #ifndef APP-NVUE
	// vue版本的组件进场处理
	const vueEnter = () => {
		// 动画进入时的类名
		const classNames = getClassNames('zoom-in-top');
		// 定义状态和发出动画进入前事件
		display = true;
		classes.value = classNames.enter;
		viewStyle.value = {}
		nextTick(async () => {
			// #ifdef H5
			await sleep(20);
			// #endif
			transitionEnded = false;

			// 赋予组件enter-to类名
			classes.value = classNames["enter-to"];
		});
	};
	// 动画离场处理
	const vueLeave = () => {
		// 如果不是展示状态，无需执行逻辑
		if (!display) return;
		const classNames = getClassNames('zoom-in-top');

		// 标记离开状态和发出事件
		// 获得类名
		classes.value = classNames.leave;

		nextTick(() => {
			// 动画正在离场的状态
			transitionEnded = false;
			// 组件执行动画，到了执行的执行时间后，执行一些额外处理
			setTimeout(onTransitionEnd, Number(props.duration));
			classes.value = classNames["leave-to"];
		});
	};
	// #endif
	// #ifdef APP-NVUE
	// nvue版本动画进场
	const nvueEnter = () => {
		// 获得样式的名称
		const currentStyle = getStyle('zoom-in-top');
		// 展示生成组件元素
		display = true;
		// 在nvue安卓上，由于渲染速度慢，在弹窗，键盘，日历等组件中，渲染其中的内容需要时间
		// 导致出现弹窗卡顿，这里让其一开始为透明状态，等一定时间渲染完成后，再让其隐藏起来，再让其按正常逻辑出现
		viewStyle.value = {
			opacity: 0,
		};
		// 等待弹窗内容渲染完成
		nextTick(() => {
			// 合并样式
			viewStyle.value = currentStyle.enter;
			Promise.resolve()
				.then(_nextTick)
				.then(() => {
					// nvue的transition动画模块需要通过ref调用组件，注意此处的ref不同于vue的this.$refs['e-transition']用法
					animation.transition(
						eTransition.value, {
						styles: currentStyle["enter-to"],
						duration: props.duration,
						timingFunction: props.timingFunction,
						needLayout: false,
						delay: 0,
					}
					);
				})
				.catch(() => { });
		});
	};
	const nvueLeave = () => {
		if (!display) {
			return;
		}
		const currentStyle = getStyle('zoom-in-top');
		// 合并样式
		viewStyle.value = currentStyle.leave;
		// 放到promise中处理执行过程
		Promise.resolve()
			.then(_nextTick) // 等待几十ms
			.then(() => {
				transitionEnded = false;
				animation.transition(
					eTransition.value, {
					styles: currentStyle["leave-to"],
					duration: props.duration,
					timingFunction: props.timingFunction,
					needLayout: false,
					delay: 0,
				},
					() => {
						onTransitionEnd();
					}
				);
			})
			.catch(() => { });
	};
	// #endif
	// 完成过渡后触发
	const onTransitionEnd = () => {
		// 如果已经是结束的状态，无需再处理
		if (transitionEnded) return;
		transitionEnded = true;
		if (!props.show && display) {
			display = false;
		}
	};

	watch(
		() => props.show,
		(newVal) => {
			// vue和nvue分别执行不同的方法
			// #ifdef APP-NVUE
			newVal ? nvueEnter() : nvueLeave();
			// #endif
			// #ifndef APP-NVUE
			newVal ? vueEnter() : vueLeave();
			// #endif
		}, {
		immediate: true,
	}
	);
</script>

<style lang="scss" scoped>
	@import '../../libs/theme/transition.scss';
</style>